/**
 * @author NamNV
 */
package vn.nt.healthcare.server.ws.orm.hibernate;

import java.util.HashMap;
import java.util.Map;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import vn.nt.healthcare.server.ws.model.Customer;
import vn.nt.healthcare.server.ws.model.Doctor;
import vn.nt.healthcare.server.ws.model.Feedback;
import vn.nt.healthcare.server.ws.model.Personal;
import vn.nt.healthcare.server.ws.model.Pregnant;
import vn.nt.healthcare.server.ws.model.Push;
import vn.nt.healthcare.server.ws.model.Reports;
import vn.nt.healthcare.server.ws.model.Schedule;
import vn.nt.healthcare.server.ws.model.Services;
import vn.nt.healthcare.server.ws.model.Sessions;
import vn.nt.healthcare.server.ws.model.Subjects;
import vn.nt.healthcare.server.ws.model.TimeSpecial;

public class HibernateUtil {
	private static Logger LOG = LoggerFactory.getLogger(HibernateUtil.class);
	private static final Map<String, SessionFactory> sessionFactoryMap = new HashMap<String, SessionFactory>();

	private static SessionFactory buildSessionFactory(String host, String port,
			String dbName, String username, String password) {
		try {
			Configuration cfg = new Configuration()
					.addAnnotatedClass(Doctor.class)
					.addAnnotatedClass(Services.class)
					.addAnnotatedClass(Subjects.class)
					.addAnnotatedClass(Personal.class)
					.addAnnotatedClass(Sessions.class)
					.addAnnotatedClass(Reports.class)
					.addAnnotatedClass(Schedule.class)
					.addAnnotatedClass(TimeSpecial.class)
					.addAnnotatedClass(Feedback.class)
					.addAnnotatedClass(Customer.class)
					.addAnnotatedClass(Pregnant.class)
					.addAnnotatedClass(Push.class)
					.setProperty("hibernate.connection.driver_class",
							"com.mysql.jdbc.Driver")
					.setProperty("hibernate.connection.provider_class",
							"com.jolbox.bonecp.provider.BoneCPConnectionProvider")
					.setProperty(
							"hibernate.connection.url",
							"jdbc:mysql://"
									+ host
									+ ":"
									+ port
									+ "/"
									+ dbName
									+ "?autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=yes&characterEncoding=UTF-8")
					.setProperty("hibernate.connection.username", username)
					.setProperty("hibernate.connection.password", password)
					.setProperty("hibernate.dialect",
							"org.hibernate.dialect.MySQL5Dialect")
					.setProperty("hibernate.current_session_context_class",
							"thread")
					.setProperty("hibernate.show_sql", "true")
					.setProperty("hibernate.connection.zeroDateTimeBehavior",
							"convertToNull")
					.setProperty("hibernate.connection.release_mode",
							"on_close")
					.setProperty("connection.release_mode", "on_close")
					.setProperty("hibernate.connection.isolation", "2")
					.setProperty("hibernate.dbcp.initialSize", "10")
					.setProperty("hibernate.dbcp.whenExhaustedAction", "1")
					.setProperty("hibernate.dbcp.maxWait", "20000")
					.setProperty("hibernate.dbcp.maxActive", "1000")
					.setProperty("hibernate.dbcp.maxIdle", "1000")
					.setProperty("hibernate.dbcp.minIdle", "10")
					.setProperty("hibernate.dbcp.validationQuery", "SELECT 1+1")
					.setProperty("hibernate.dbcp.testOnBorrow", "true")
					.setProperty("hibernate.dbcp.testOnReturn", "true")
					.setProperty("dbcp.initialSize", "10")
					.setProperty("dbcp.whenExhaustedAction", "1")
					.setProperty("dbcp.maxWait", "20000")
					.setProperty("dbcp.maxActive", "1000")
					.setProperty("dbcp.maxIdle", "1000")
					.setProperty("dbcp.minIdle", "10")
					.setProperty("dbcp.validationQuery", "SELECT 1+1")
					.setProperty("dbcp.testOnBorrow", "true")
					.setProperty("dbcp.testOnReturn", "true")
					.setProperty("bonecp.idleMaxAgeInMinutes", "70")
					.setProperty("bonecp.idleConnectionTestPeriodInMinutes",
							"65")
					.setProperty("bonecp.partitionCount", "5")
					.setProperty("bonecp.acquireIncrement", "3")
					.setProperty("bonecp.maxConnectionsPerPartition", "50")
					.setProperty("bonecp.minConnectionsPerPartition", "2")
					.setProperty("bonecp.maxConnectionAgeInSeconds", "3600")
					.setProperty("bonecp.statementsCacheSize", "0")
					.setProperty("bonecp.connectionTimeoutInMs", "1000")
					.setProperty("bonecp.connectionTestStatement",
							"SELECT 1 FROM DUAL");
			cfg.registerTypeOverride(new LowerCaseYesNoType());
			ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
					.applySettings(cfg.getProperties()).buildServiceRegistry();
			SessionFactory sessionFactory = cfg
					.buildSessionFactory(serviceRegistry);
			return sessionFactory;
		} catch (Throwable ex) {
			System.err.println("Initial SessionFactory creation failed." + ex);
			throw new ExceptionInInitializerError(ex);
		}
	}

	public static SessionFactory getSessionFactory(String host, String port,
			String dbName, String username, String password) {
		String key = host + dbName + username + password;
		SessionFactory sessionFactory = sessionFactoryMap.get(key);
		if (sessionFactory != null) {
			LOG.info("use existing session");
			return sessionFactory;
		} else {
			LOG.info("Initialize new session");
			sessionFactory = buildSessionFactory(host, port, dbName, username,
					password);
			sessionFactoryMap.put(key, sessionFactory);
		}
		return sessionFactory;
	}

	public static void removeSessionFactory(String host, String dbName,
			String username, String password) {
		String key = host + dbName + username + password;
		sessionFactoryMap.remove(key);
	}
}
